/*
 * Copyright (C) 2009,2010,2011  Paulo Cesar Pereira de Andrade.
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * Authors:
 *	Paulo Cesar Pereira de Andrade
 */

#ifndef _ethread_h
#define _ethread_h

#include "etypes.h"

#include <setjmp.h>

#if HAVE_THREAD
#  define emutex_lock(mutex)						\
    do {								\
	if (pthread_mutex_lock(mutex))					\
	    eerror("pthread_mutex_lock: %s", strerror(errno));		\
    } while (0)

#  define emutex_unlock(mutex)						\
    do {								\
	if (pthread_mutex_unlock(mutex))				\
	    eerror("pthread_mutex_unlock: %s", strerror(errno));	\
    } while (0)

#  define emutex_init(mutex)						\
    do {								\
	if ((errno = pthread_mutex_init(mutex, null)))			\
	    eerror("pthread_mutex_init: %s", strerror(errno));		\
    } while (0)

#  define emutex_destroy(mutex)						\
    do {								\
	if ((errno = pthread_mutex_destroy(mutex)))			\
	    eerror("pthread_mutex_destroy: %s", strerror(errno));	\
    } while (0)

#  define ethreads_lock()	emutex_lock(&ethread_mutex)
#  define ethreads_unlock()	emutex_unlock(&ethread_mutex)

#  define ethread_kill(signo)						\
    do {								\
        pthread_kill(thread_self->pthread, signo);			\
    } while (0)

#else
#  define emutex_lock(mutex)	/**/
#  define emutex_unlock(mutex)	/**/
#  define emutex_init(mutex)	/**/
#  define emutex_destroy(mutex)	/**/
#  define ethreads_lock()	/**/
#  define ethreads_unlock()	/**/

#  define ethread_kill(signo)						\
    do {								\
        kill(getpid(), signo);						\
    } while (0)
#endif

#define eraise_exception(value)	evm_raise(value)

/*
 * Types
 */
struct ethread {
    evalue_t	 r0;
    evalue_t	*ess;		/* stack memory */
    evalue_t	*ebp;		/* base pointer */
    evalue_t	*esp;		/* stack pointer */
    eint32_t	 len;		/* stack length */
    evalue_t	 ret;		/* builtin return */
    mpfr_rnd_t	 rnd;		/* mpfr round mode */
    mp_prec_t	 prc;		/* mpfr/mpc precision */
    ebool_t	 flt;		/* float format: 0 double, nonzero mpfr_t */

    __cqq_struct	qq;
    __mpc_struct	c;

    __mpq_struct	q0;
    __mpf_struct	f0;
    __mpc_struct	c0;
    __mpq_struct	q1;
    __mpq_struct	q2;
    __mpq_struct	q3;

    /* result of last memory allocation, that should be gc protected,
     * as another thread may cause gc to trigger before the value is
     * actually used */
    eobject_t	 obj;

    empcache_t	 c_z;
    empcache_t	 c_q;
    empcache_t	 c_r;
    empcache_t	 c_c;
    empcache_t	 c_qq;

    /* random usage temporary vector */
    evector_t	*vec;

    /* thread gc protected data */
    eobject_t	*egs;
    eint32_t	 gsi;
    eint32_t	 gsl;

    /* varargs count */
    eint32_t	 vcount;

    /* exception information */
    eint32_t	 tryoff;
    evalue_t	 evalue;
    eexcept_t	 except;

    /* used to specify:
     * o the start address of a thread
     * o the "current" address in language context when printing a backtrace
     */
    eobject_t	 eip;

#if HAVE_THREAD
    ethread_t	*next;		/* next thread */

    pthread_t	 pthread;
#endif

    eint32_t	 running : 1;

    sigjmp_buf	 env;		/* exceptions */
};

#if HAVE_THREAD
/*
 * Prototypes
 */
extern void
init_thread(void);

/*
 * Externs
 */
extern __thread ethread_t *thread_self;
#else
#define thread_self		tmain
#endif

#endif /* _ethread_h */
